package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"path/filepath"
	"syscall"
	"time"
	"unsafe"

	"monitor/models"
	"monitor/websocket"

	"github.com/shirou/gopsutil/v3/cpu"
	"github.com/shirou/gopsutil/v3/net"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var (
	user32                   = syscall.NewLazyDLL("user32.dll")
	kernel32                 = syscall.NewLazyDLL("kernel32.dll")
	psapi                    = syscall.NewLazyDLL("psapi.dll")
	setWindowsHookEx         = user32.NewProc("SetWindowsHookExW")
	callNextHookEx           = user32.NewProc("CallNextHookEx")
	unhookWindowsHookEx      = user32.NewProc("UnhookWindowsHookEx")
	getMessage               = user32.NewProc("GetMessageW")
	mapVirtualKey            = user32.NewProc("MapVirtualKeyW")
	getKeyState              = user32.NewProc("GetKeyState")
	getKeyNameText           = user32.NewProc("GetKeyNameTextW")
	getForegroundWindow      = user32.NewProc("GetForegroundWindow")
	getWindowThreadProcessId = user32.NewProc("GetWindowThreadProcessId")
	getWindowTextW           = user32.NewProc("GetWindowTextW")
	openProcess              = kernel32.NewProc("OpenProcess")
	getModuleFileNameExW     = psapi.NewProc("GetModuleFileNameExW")
	closeHandle              = kernel32.NewProc("CloseHandle")
	setWinEventHook          = user32.NewProc("SetWinEventHook")
	unhookWinEvent           = user32.NewProc("UnhookWinEvent")
	keyboardHook             uintptr
	windowEventHook          uintptr
)

const (
	WH_KEYBOARD_LL            = 13
	WM_KEYDOWN                = 0x0100
	WM_SYSKEYDOWN             = 0x0104
	WM_KEYUP                  = 0x0101
	WM_SYSKEYUP               = 0x0105
	MAPVK_VK_TO_CHAR          = 2
	PROCESS_QUERY_INFORMATION = 0x0400
	PROCESS_VM_READ           = 0x0010
	EVENT_SYSTEM_FOREGROUND   = 0x0003
	WINEVENT_OUTOFCONTEXT     = 0x0000
)

type KBDLLHOOKSTRUCT struct {
	VkCode      uint32
	ScanCode    uint32
	Flags       uint32
	Time        uint32
	DwExtraInfo uintptr
}

func getKeyName(vkCode uint32, scanCode uint32) string {
	// 获取按键名称
	var buffer [256]uint16
	scanCode = (scanCode << 16) | 1 // 添加扩展位
	getKeyNameText.Call(uintptr(scanCode), uintptr(unsafe.Pointer(&buffer[0])), 256)
	name := syscall.UTF16ToString(buffer[:])

	// 处理特殊按键
	switch vkCode {
	case 0x10:
		return "SHIFT"
	case 0x11:
		return "CTRL"
	case 0x12:
		return "ALT"
	case 0x14:
		return "CAPS"
	case 0x1B:
		return "ESC"
	case 0x20:
		return ""
	case 0x09:
		return "TAB"
	case 0x0D:
		return "ENTER"
	case 0x08:
		return "←"
	case 0x25:
		return "←"
	case 0x26:
		return "↑"
	case 0x27:
		return "→"
	case 0x28:
		return "↓"
	case 0x2E:
		return "DEL"
	case 0x23:
		return "END"
	case 0x21:
		return "PGUP"
	case 0x22:
		return "PGDN"
	case 0x24:
		return "HOME"
	case 0x2C:
		return "PRT\nSC"
	case 0x91:
		return "SCR\nLK"
	case 0x13:
		return "PAUSE"
	case 0x5B:
		return "WIN"
	}

	if name != "" {
		return name
	}
	return fmt.Sprintf("未知按键(%d)", vkCode)
}

var (
	db               *gorm.DB
	keyPressMap      map[string]*models.KeyPress
	lastKeyPressTime time.Time
	lastAppInfo      *models.AppSwitchDetail // 添加上一次应用信息的变量
)

// broadcastKeyPressEvent 广播按键事件
func broadcastKeyPressEvent(keyName string, count int) {
	event := &models.Event{
		Type:      models.KeyPressEvent,
		Timestamp: time.Now(),
		Data: models.KeyPressEventData{
			KeyName: keyName,
			Count:   count,
		},
	}
	fmt.Println(event)
	websocket.GetBroadcastManager().BroadcastEvent(event)
}

// broadcastAppSwitchEvent 广播应用切换事件
func broadcastAppSwitchEvent(sourceApp, targetApp, sourceTitle, targetTitle string) {
	event := &models.Event{
		Type:      models.AppSwitchEvent,
		Timestamp: time.Now(),
		Data: models.AppSwitchEventData{
			SourceAppName:     sourceApp,
			SourceWindowTitle: sourceTitle,
			TargetAppName:     targetApp,
			TargetWindowTitle: targetTitle,
		},
	}
	websocket.GetBroadcastManager().BroadcastEvent(event)
}

// broadcastNetworkTrafficEvent 广播网络流量事件
func broadcastNetworkTrafficEvent(uploadDelta, downloadDelta float64) {
	event := &models.Event{
		Type:      models.NetworkTrafficEvent,
		Timestamp: time.Now(),
		Data: models.NetworkTrafficEventData{
			UploadDelta:   uploadDelta,
			DownloadDelta: downloadDelta,
		},
	}
	websocket.GetBroadcastManager().BroadcastEvent(event)
}

var keyboardHookCallback = syscall.NewCallback(func(nCode int, wparam uintptr, lparam uintptr) uintptr {
	if nCode >= 0 {
		kbdStruct := (*KBDLLHOOKSTRUCT)(unsafe.Pointer(lparam))
		vkCode := kbdStruct.VkCode
		scanCode := kbdStruct.ScanCode
		keyName := getKeyName(vkCode, scanCode)

		if wparam == WM_KEYDOWN || wparam == WM_SYSKEYDOWN {
			fmt.Printf("按下按键: %s\n", keyName)

			// 广播按键按下事件
			broadcastKeyPressEvent(keyName, 1)

			// 处理按键统计
			currentTime := time.Now()
			if currentTime.Sub(lastKeyPressTime) >= time.Second {
				// 如果距离上次记录超过1秒，保存所有按键记录并重置计数
				for _, keyPress := range keyPressMap {
					if err := db.Create(keyPress).Error; err != nil {
						fmt.Printf("记录按键数据失败: %v\n", err)
					}
				}
				// 重置按键映射和时间
				keyPressMap = make(map[string]*models.KeyPress)
				lastKeyPressTime = currentTime
			}

			// 更新或创建按键记录
			if keyPress, exists := keyPressMap[keyName]; exists {
				keyPress.Count++
			} else {
				keyPressMap[keyName] = &models.KeyPress{
					Timestamp: currentTime,
					KeyName:   keyName,
					Count:     1,
				}
			}
		} else if wparam == WM_KEYUP || wparam == WM_SYSKEYUP {
			// 广播按键释放事件
			broadcastKeyPressEvent(keyName, 0)
		}
	}
	ret, _, _ := callNextHookEx.Call(keyboardHook, uintptr(nCode), wparam, lparam)
	return ret
})

func keyboardListener(done chan bool) {
	// 设置键盘钩子
	keyboardHook, _, _ = setWindowsHookEx.Call(
		WH_KEYBOARD_LL,
		keyboardHookCallback,
		0,
		0,
	)

	if keyboardHook == 0 {
		fmt.Println("键盘钩子设置失败")
		done <- true
		return
	}

	// 消息循环
	var msg struct {
		HWND   uintptr
		Msg    uint32
		WParam uintptr
		LParam uintptr
		Time   uint32
		Pt     struct{ X, Y int32 }
	}

	defer unhookWindowsHookEx.Call(keyboardHook)

	for {
		getMessage.Call(uintptr(unsafe.Pointer(&msg)), 0, 0, 0)
	}
}

// getForegroundWindowInfo 获取前台窗口信息
func getForegroundWindowInfo() (string, string, string) {
	// 获取前台窗口句柄
	hwnd, _, _ := getForegroundWindow.Call()
	if hwnd == 0 {
		return "", "", ""
	}

	// 获取窗口标题
	var title [256]uint16
	getWindowTextW.Call(hwnd, uintptr(unsafe.Pointer(&title[0])), 256)
	windowTitle := syscall.UTF16ToString(title[:])

	// 获取进程ID
	var processID uint32
	getWindowThreadProcessId.Call(hwnd, uintptr(unsafe.Pointer(&processID)))

	// 打开进程
	processHandle, _, _ := openProcess.Call(
		PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
		0,
		uintptr(processID),
	)
	if processHandle == 0 {
		return windowTitle, "", ""
	}
	defer closeHandle.Call(processHandle)

	// 获取进程可执行文件路径
	var exePath [260]uint16
	getModuleFileNameExW.Call(
		processHandle,
		0,
		uintptr(unsafe.Pointer(&exePath[0])),
		260,
	)
	exeFullPath := syscall.UTF16ToString(exePath[:])

	// 获取应用名称（从可执行文件路径中提取）
	appName := filepath.Base(exeFullPath)

	return windowTitle, appName, exeFullPath
}

var windowEventCallback = syscall.NewCallback(func(hWinEventHook uintptr, event uint32, hwnd uintptr, idObject int32, idChild int32, idEventThread uint32, dwmsEventTime uint32) uintptr {
	if event == EVENT_SYSTEM_FOREGROUND {
		windowTitle, appName, exeFullPath := getForegroundWindowInfo()
		if appName == "" {
			return 0
		}

		// 打印信息
		fmt.Printf("\n当前前台应用:\n")
		fmt.Printf("窗口标题: %s\n", windowTitle)
		fmt.Printf("应用名称: %s\n", appName)
		fmt.Printf("可执行文件路径: %s\n\n", exeFullPath)

		currentTime := time.Now()

		// 更新应用切换记录
		var appSwitch models.AppSwitch
		result := db.Where("app_name = ?", appName).First(&appSwitch)

		if result.Error != nil {
			// 如果应用不存在，创建新记录
			appSwitch = models.AppSwitch{
				Timestamp:      currentTime,
				AppName:        appName,
				WindowTitle:    windowTitle,
				ExePath:        exeFullPath,
				SwitchCount:    1,
				LastActiveTime: currentTime,
				UsageDuration:  0,
			}
		} else {
			// 如果应用已存在，更新切换次数、使用时长和最后活动时间
			appSwitch.SwitchCount++
			// 计算并更新使用时长（秒）
			usageDuration := currentTime.Sub(appSwitch.LastActiveTime).Seconds()
			appSwitch.UsageDuration += int64(usageDuration)
			appSwitch.LastActiveTime = currentTime
			appSwitch.WindowTitle = windowTitle // 更新窗口标题
		}

		if err := db.Save(&appSwitch).Error; err != nil {
			fmt.Printf("保存应用切换记录失败: %v\n", err)
		}

		// 记录应用切换流水
		if lastAppInfo != nil && lastAppInfo.TargetAppName != appName {
			// 创建新的切换流水记录
			switchDetail := models.AppSwitchDetail{
				Timestamp:         currentTime,
				SourceAppName:     lastAppInfo.TargetAppName,
				SourceWindowTitle: lastAppInfo.TargetWindowTitle,
				SourceExePath:     lastAppInfo.TargetExePath,
				TargetAppName:     appName,
				TargetWindowTitle: windowTitle,
				TargetExePath:     exeFullPath,
			}

			if err := db.Create(&switchDetail).Error; err != nil {
				fmt.Printf("保存应用切换流水记录失败: %v\n", err)
			}

			// 广播应用切换事件
			broadcastAppSwitchEvent(
				switchDetail.SourceAppName,
				switchDetail.TargetAppName,
				switchDetail.SourceWindowTitle,
				switchDetail.TargetWindowTitle,
			)
		}

		// 更新上一次应用信息
		if lastAppInfo == nil {
			lastAppInfo = &models.AppSwitchDetail{}
		}
		lastAppInfo.TargetAppName = appName
		lastAppInfo.TargetWindowTitle = windowTitle
		lastAppInfo.TargetExePath = exeFullPath
	}
	return 0
})

func windowMonitor(done chan bool) {
	// 设置窗口事件钩子
	windowEventHook, _, _ = setWinEventHook.Call(
		EVENT_SYSTEM_FOREGROUND, // eventMin
		EVENT_SYSTEM_FOREGROUND, // eventMax
		0,                       // hmodWinEventProc
		windowEventCallback,     // lpfnWinEventProc
		0,                       // idProcess
		0,                       // idThread
		WINEVENT_OUTOFCONTEXT,   // dwFlags
	)

	if windowEventHook == 0 {
		fmt.Println("窗口事件钩子设置失败")
		done <- true
		return
	}

	defer unhookWinEvent.Call(windowEventHook)

	// 消息循环
	var msg struct {
		HWND   uintptr
		Msg    uint32
		WParam uintptr
		LParam uintptr
		Time   uint32
		Pt     struct{ X, Y int32 }
	}

	for {
		ret, _, _ := getMessage.Call(uintptr(unsafe.Pointer(&msg)), 0, 0, 0)
		if ret == 0 {
			break
		}
	}
}

func initDB() error {
	var err error
	db, err = gorm.Open(sqlite.Open("network_traffic.db"), &gorm.Config{})
	if err != nil {
		return fmt.Errorf("连接数据库失败: %v", err)
	}

	// 自动迁移数据库结构
	err = db.AutoMigrate(&models.NetworkTraffic{}, &models.NetworkTrafficDelta{}, &models.KeyPress{}, &models.AppSwitch{}, &models.AppSwitchDetail{})
	if err != nil {
		return fmt.Errorf("数据库迁移失败: %v", err)
	}

	// 初始化按键映射
	keyPressMap = make(map[string]*models.KeyPress)
	lastKeyPressTime = time.Now()

	return nil
}

func networkTrafficMonitor(_ chan bool) {
	lastRecord := time.Now()
	var lastBytesSent, lastBytesRecv float64

	for {
		// 使用 gopsutil 获取网络接口统计信息
		ioStats, err := net.IOCounters(false) // false 表示获取所有接口的汇总数据
		if err != nil {
			fmt.Printf("获取网络统计信息失败: %v\n", err)
			time.Sleep(time.Second)
			continue
		}

		// 由于我们使用 false 参数，IOCounters 会返回一个只包含一个元素的切片
		// 这个元素包含了所有网络接口的汇总数据
		if len(ioStats) > 0 {
			totalStats := ioStats[0]
			totalBytesSent := float64(totalStats.BytesSent) / (1024 * 1024)
			totalBytesRecv := float64(totalStats.BytesRecv) / (1024 * 1024)
			fmt.Printf("网络流量 - 累计下载: %.2f MB, 累计上传: %.2f MB\n", totalBytesRecv, totalBytesSent)

			// 计算并广播流量变化
			uploadDelta := totalBytesSent - lastBytesSent
			downloadDelta := totalBytesRecv - lastBytesRecv
			broadcastNetworkTrafficEvent(uploadDelta, downloadDelta)

			// 每分钟记录一次数据库
			if time.Since(lastRecord) >= time.Minute {
				// 记录总流量
				traffic := models.NetworkTraffic{
					Timestamp:     time.Now(),
					UploadBytes:   totalBytesSent,
					DownloadBytes: totalBytesRecv,
				}

				if err := db.Create(&traffic).Error; err != nil {
					fmt.Printf("记录网络流量数据失败: %v\n", err)
				}

				// 记录流量变化
				trafficDelta := models.NetworkTrafficDelta{
					Timestamp:     time.Now(),
					UploadDelta:   uploadDelta,
					DownloadDelta: downloadDelta,
				}

				if err := db.Create(&trafficDelta).Error; err != nil {
					fmt.Printf("记录网络流量变化数据失败: %v\n", err)
				}

				lastRecord = time.Now()
			}

			lastBytesSent = totalBytesSent
			lastBytesRecv = totalBytesRecv
		}

		time.Sleep(time.Second)
	}
}

func cpuMonitor(_ chan bool) {
	for {
		// 获取CPU使用率
		percentages, err := cpu.Percent(time.Second, true) // true表示获取每个CPU核心的使用率
		if err != nil {
			fmt.Printf("获取CPU使用率失败: %v\n", err)
			continue
		}

		// 获取总体CPU使用率
		totalPercent, err := cpu.Percent(time.Second, false)
		if err != nil {
			fmt.Printf("获取总体CPU使用率失败: %v\n", err)
			continue
		}

		// 打印总体CPU使用率
		fmt.Printf("\nCPU使用率 - 总体: %.2f%%\n", totalPercent[0])

		// 打印每个核心的使用率
		for i, percent := range percentages {
			fmt.Printf("CPU核心 #%d: %.2f%%\n", i, percent)
		}
	}
}

func keyPressSummary(w http.ResponseWriter, r *http.Request) {
	// 设置响应头
	w.Header().Set("Content-Type", "application/json")
	var results []struct {
		KeyName  string `json:"key_name"`
		KeyCount int    `json:"key_count"`
	}
	_ = db.Raw(`
	   SELECT key_name, SUM(count) as key_count
	   FROM key_presses
	   GROUP BY key_name
	`).Scan(&results).Error
	// 将结果转换为JSON并返回
	json.NewEncoder(w).Encode(results)
}
func getKeyPressCountByMinute(w http.ResponseWriter, r *http.Request) {
	// 设置响应头
	w.Header().Set("Content-Type", "application/json")

	// 查询按分钟汇总的按键次数
	var results []struct {
		Minute   string `json:"minute"`
		KeyName  string `json:"key_name"`
		KeyCount int    `json:"key_count"`
	}

	err := db.Raw(`
        SELECT 
            strftime('%Y-%m-%d %H:%M:00', timestamp) as minute,
            key_name,
            SUM(count) as key_count
        FROM key_presses
        GROUP BY minute, key_name
        ORDER BY minute DESC, key_count DESC
    `).Scan(&results).Error

	if err != nil {
		http.Error(w, fmt.Sprintf("查询数据失败: %v", err), http.StatusInternalServerError)
		return
	}

	// 将结果转换为JSON并返回
	json.NewEncoder(w).Encode(results)
}

func main() {
	fmt.Println("开始监听键盘事件和前台窗口...")
	// 初始化数据库
	if err := initDB(); err != nil {
		fmt.Printf("初始化数据库失败: %v\n", err)
		return
	}

	// 创建一个channel用于同步
	done := make(chan bool)

	// 在goroutine中启动键盘监听
	go keyboardListener(done)

	// 在goroutine中启动窗口监控
	go windowMonitor(done)

	// 在goroutine中启动网络流量监控
	// go networkTrafficMonitor(done)

	// 设置HTTP路由
	http.HandleFunc("/api/keypress/minutes", getKeyPressCountByMinute)
	http.HandleFunc("/api/keypress/summary", keyPressSummary)

	// 在goroutine中启动HTTP服务器
	go func() {
		if err := http.ListenAndServe(":8081", nil); err != nil {
			fmt.Printf("启动HTTP服务器失败: %v\n", err)
			done <- true
		}
	}()

	// 在goroutine中启动WebSocket服务器
	go func() {
		if err := websocket.StartWebSocketServer(8080); err != nil {
			fmt.Printf("启动WebSocket服务器失败: %v\n", err)
			done <- true
		}
	}()

	// 等待程序结束信号
	<-done
}
